import { Base } from '../base.js'
import * as THREE from 'three'
import TWEEN from '../utils/tween.js'

export class Pic extends Base {
	group = new THREE.Group()
	clock = new THREE.Clock()

	constructor(id: string) {
		super(id, false)
		this.camera.position.set(0, 0, 150)
		// this.controls.enabled = false
	}
	getEuler(angle: number, r: number) {
		const a = new THREE.Euler(THREE.MathUtils.degToRad(Math.random() * 360), THREE.MathUtils.degToRad(angle), THREE.MathUtils.degToRad(Math.random() * 360), 'XYZ')
		const b = new THREE.Vector3(r, 0, 0)
		b.applyEuler(a)
		return b
	}
	initPoints() {
		const uniforms = {
			pointTexture: {
				value: new THREE.TextureLoader().load(
					'images/sprites/spark1.png'
				)
			},
            opacity: {
                value: 1.0
            }
		}
		const shaderMaterial = new THREE.ShaderMaterial({
			uniforms: uniforms,
			vertexShader: `
                attribute float size;
                varying vec3 vColor;
                void main() {
                    vColor = color;
                    vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );
                    gl_PointSize = size * ( 300.0 / -mvPosition.z );
                    gl_Position = projectionMatrix * mvPosition;
                }
            `,
			fragmentShader: `
                uniform sampler2D pointTexture;
                uniform float opacity;
                varying vec3 vColor;
                void main() {
                    gl_FragColor = vec4( vColor, opacity );
                    gl_FragColor = gl_FragColor * texture2D( pointTexture, gl_PointCoord );
                }
            `,
			blending: THREE.AdditiveBlending,
			depthTest: false,
			transparent: true,
			vertexColors: true
		})
		const radius = 40

		const geometry = new THREE.BufferGeometry()

		const positions: number[] = []
		const colors = []
		const sizes = []
		const savePositions: number[] = []

		const color = new THREE.Color()
		const particles = 200

		for (let i = 0; i < particles; i++) {
			const p = this.getEuler((360 / particles) * i, radius)
			savePositions.push(p.x)
			savePositions.push(p.y)
			savePositions.push(p.z)
			positions.push(0, 0, 0)
			color.setHSL(i / particles, 1.0, 0.5)

			colors.push(color.r, color.g, color.b)

			sizes.push(20)
		}

		geometry.setAttribute(
			'position',
			new THREE.Float32BufferAttribute(positions, 3)
		)
		geometry.setAttribute(
			'color',
			new THREE.Float32BufferAttribute(colors, 3)
		)
		geometry.setAttribute(
			'size',
			new THREE.Float32BufferAttribute(sizes, 1).setUsage(
				THREE.DynamicDrawUsage
			)
		)

		const particleSystem = new THREE.Points(geometry, shaderMaterial)
		this.scene.add(particleSystem)
       

        const runing = async () => {
            const y = Math.random() * 50
            particleSystem.position.setX(Math.random() * 200 - 100)
            particleSystem.position.setY(y - 100)
            uniforms.opacity.value = 1.0
            await this.runing({ y: y - 100}, {y}, data => {
                particleSystem.position.setY(data.y)
            }, Math.random() * 1500 + 500)
            const pos = [...positions]
            new TWEEN.Tween(pos)
		    .to(savePositions, 1000)
		    .easing((amount) => TWEEN.Easing.Quadratic.Out(amount))
		    .onUpdate(() => {
		        geometry.setAttribute( 'position', new THREE.Float32BufferAttribute( pos, 3 ) );
		    }).onComplete(() => {
                geometry.setAttribute( 'position', new THREE.Float32BufferAttribute( positions, 3 ) );
                setTimeout(() => runing(), 100)
		    }).start()
            this.runing({
                opacity: 1,
                ry: THREE.MathUtils.degToRad(0)
            }, { 
                opacity: 0,
                ry: THREE.MathUtils.degToRad(30)
            }, p => {
                uniforms.opacity.value = p.opacity
                particleSystem.rotation.y = p.ry
            }, 1000, 'none')
        }
		runing()
	}
    runing(start:any, end:any, call:(start:any) => void, time:number|undefined = 1000, type?:string) {
        return new Promise<any>((resolve, reject) => {
            const t = new TWEEN.Tween(start)
            .to(end, time)
            
            .onUpdate(() => {
                call(start)
            }).onComplete(() => {
                resolve(true)
            }).start()
            if (type!=='none') {
                t.easing((amount) => TWEEN.Easing.Quadratic.Out(amount))
            }
            
        })
    }
	async initObject() {
		for(let i=0;i<10;i++) {
            this.initPoints()
        }
	}
	onRender() {
		TWEEN.update()
	}
}
